﻿/**
 * File:   aidl_parser.c
 * Author: AWTK Develop Team
 * Brief:  aidl_parser
 *
 * Copyright (c) 2018 - 2020  Guangzhou ZHIYUAN Electronics Co.,Ltd.
 *
 * This program is ditokenizeributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * License file for more details.
 *
 */

/**
 * History:
 * ================================================================
 * 2020-12-09 zhang zhongji <zhangzhongji@zlg.cn> created
 *
 */

#include "aidl_parser.h"

/**
 * @class aidl_parser_t
 * 用于解析aidl文件。
 * 
 * ```c
 * aidl_parser_t aidl_parser;
 * const char* aidl = "package org.kde.necessitas.ministro;\
 *import org.kde.necessitas.ministro.IMinistroCallback;\
 *\
 *interface IMinistro\
 *{\
 *  void requestLoader(in IMinistroCallback callback, in Bundle parameters);\
 *   void loaderReady(in Bundle loaderParams);\
 *}";
 *
 * aidl_parser_t* t = aidl_parser_init(&aidl_parser, aidl, 0xffffff, ",");
 *
 * while(aidl_parser_has_more(t)) {
 *   const char* token = aidl_parser_next(t);
 *   log_debug("%s\n", token);
 * }
 *
 * aidl_parser_deinit(t);
 * ```
 */
typedef struct _aidl_parser_t {
  /**
   * @property {char*} str
   * @annotation ["readable"]
   * 字符串。
   */
  const char* str;
  /**
   * @property {uint32_t} size
   * @annotation ["readable"]
   * 字符串的长度。
   */
  uint32_t size;
  /**
   * @property {uint32_t} cursor
   * @annotation ["readable"]
   * 当前位置。
   */
  uint32_t cursor;

  /**
   * @property {char*} separator
   * @annotation ["readable"]
   * 分隔字符串。
   */
  const char* separator;

  str_t token;

  /**
   * @property {char*} builder
   * @annotation ["readable"]
   * 构造器。
   */
  builder_t* builder;
} aidl_parser_t;

static aidl_parser_t* aidl_parser_init(aidl_parser_t* aidl_parser, const char* str, uint32_t size, const char* separator) {
    uint32_t str_size = 0;
    return_value_if_fail(aidl_parser != NULL && str != NULL, NULL);

    str_size = strlen(str);
    aidl_parser->str = str;
    aidl_parser->cursor = 0;
    aidl_parser->separator = separator;
    aidl_parser->size = tk_min(str_size, size);
    aidl_parser->builder = NULL;
    str_init(&(aidl_parser->token), 20);

    return aidl_parser;
}

static bool_t aidl_parser_has_more(aidl_parser_t* aidl_parser) {
  return_value_if_fail(aidl_parser != NULL, FALSE);

  return aidl_parser->cursor < aidl_parser->size;
}

static aidl_parser_t* aidl_parser_set_builder(aidl_parser_t* aidl_parser, builder_t* builder) {
    aidl_parser->builder = builder;
    return aidl_parser;
}

static ret_t aidl_parser_skip_separator(aidl_parser_t* aidl_parser) {
    return_value_if_fail(aidl_parser != NULL, RET_BAD_PARAMS);

    while (aidl_parser->cursor < aidl_parser->size) {
        char c = aidl_parser->str[aidl_parser->cursor];
        if (strchr(aidl_parser->separator, c) != NULL) {
            aidl_parser->cursor++;
        } else {
            break;
        }
    }

    return RET_OK;
}

static ret_t aidl_parser_skip_non_separator(aidl_parser_t* aidl_parser) {
    const char* separtor = NULL;
    return_value_if_fail(aidl_parser != NULL, RET_BAD_PARAMS);

    separtor = aidl_parser->separator;

    while (aidl_parser->cursor < aidl_parser->size) {
        char c = aidl_parser->str[aidl_parser->cursor];
        if (strchr(separtor, c) == NULL) {
            aidl_parser->cursor++;
        } else {
            break;
        }
    }

    return RET_OK;
}

static ret_t aidl_parser_skip_after(aidl_parser_t* aidl_parser, const char* str) {
    return_value_if_fail(aidl_parser_skip_separator(aidl_parser) == RET_OK && str != NULL, RET_BAD_PARAMS);

    if (aidl_parser_has_more(aidl_parser) && strlen(str) > 0) {

        char *left_str = strstr(aidl_parser->str+aidl_parser->cursor, str);
        if (left_str != NULL) {
            aidl_parser->cursor = aidl_parser->size - strlen(left_str);
            aidl_parser->cursor = aidl_parser->cursor + strlen(str);
        }
    }

    return RET_OK;
}

static ret_t aidl_parser_skip_comment(aidl_parser_t* aidl_parser) {
    return_value_if_fail(aidl_parser != NULL, RET_BAD_PARAMS);

    while (aidl_parser_has_more(aidl_parser) && aidl_parser->str[aidl_parser->cursor]) {
        char c = aidl_parser->str[aidl_parser->cursor];
        if (c != '/') {
            break;
        } else {
            if (aidl_parser->size - aidl_parser->cursor >=2) {
                char c1 = aidl_parser->str[aidl_parser->cursor+1];
                if (c1 == '/') {
                    aidl_parser_skip_after(aidl_parser, "\n");
                } else {
                    aidl_parser_skip_after(aidl_parser, "*/");
                }
            }
            
            if (aidl_parser_has_more(aidl_parser)) {
                aidl_parser_skip_separator(aidl_parser);
            }
        }
    }

    return RET_OK;
}

static const char* aidl_parser_next(aidl_parser_t* aidl_parser) {
  return_value_if_fail(aidl_parser_skip_separator(aidl_parser) == RET_OK, NULL);
  return_value_if_fail(aidl_parser_skip_comment(aidl_parser) == RET_OK, NULL);

  if (aidl_parser_has_more(aidl_parser)) {
    uint32_t len = 0;
    str_t* s = &(aidl_parser->token);
    uint32_t start = aidl_parser->cursor;

    aidl_parser_skip_non_separator(aidl_parser);

    len = aidl_parser->cursor - start;
    str_set_with_len(s, aidl_parser->str + start, len);
    aidl_parser_skip_separator(aidl_parser);

    return s->str;
  }

  return NULL;
}

static const char* aidl_parser_next_until(aidl_parser_t* aidl_parser, const char* str) {
  return_value_if_fail(aidl_parser_skip_separator(aidl_parser) == RET_OK && str != NULL, NULL);
  return_value_if_fail(aidl_parser_skip_comment(aidl_parser) == RET_OK, NULL);

  if (aidl_parser_has_more(aidl_parser)) {
    uint32_t len = 0;
    str_t* s = &(aidl_parser->token);
    uint32_t start = aidl_parser->cursor;

    while (aidl_parser->str[aidl_parser->cursor]) {
      char c = aidl_parser->str[aidl_parser->cursor];
      if (strchr(str, c) != NULL) {
        break;
      }
      aidl_parser->cursor++;
    }

    len = aidl_parser->cursor - start;
    str_set_with_len(s, aidl_parser->str + start, len);
    aidl_parser_skip_separator(aidl_parser);

    return s->str;
  }

  return NULL;
}

static const char* aidl_parser_next_package(aidl_parser_t* aidl_parser) {
    while (aidl_parser_has_more(aidl_parser)) {
        const char* keyword = aidl_parser_next(aidl_parser);

        if (keyword != NULL && strcmp("package", keyword) == 0) {
            const char* package_name = aidl_parser_next(aidl_parser);
            if (package_name != NULL) {
                return package_name;
            }
        } 
    }

    return NULL;
}

static const char* aidl_parser_first_package(aidl_parser_t* aidl_parser) {
    aidl_parser->cursor = 0;
    return aidl_parser_next_package(aidl_parser);
}

static const char* aidl_parser_next_import(aidl_parser_t* aidl_parser) {
    while (aidl_parser_has_more(aidl_parser)) {
        const char* keyword = aidl_parser_next(aidl_parser);

        if (keyword != NULL && strcmp("import", keyword) == 0) {
            const char* import_name = aidl_parser_next(aidl_parser);
            if (import_name != NULL) {
                return import_name;
            }
        } 
    }

    return NULL;
}

static const char* aidl_parser_first_import(aidl_parser_t* aidl_parser) {
    aidl_parser->cursor = 0;
    return aidl_parser_next_import(aidl_parser);
}

static const char* aidl_parser_interface_name(aidl_parser_t* aidl_parser) {
    aidl_parser->cursor = 0;
    while (aidl_parser_has_more(aidl_parser)) {
        const char* keyword = aidl_parser_next(aidl_parser);

        if (keyword != NULL && strcmp("interface", keyword) == 0) {
            const char* interface_name = aidl_parser_next(aidl_parser);
            if (interface_name != NULL) {
                return interface_name;
            }
        } 
    }

    return NULL;
}

static ret_t param_component_destroy_t(void* data) {
    if (data != NULL) {
        param_component_t* param_component = (param_component_t*)data;
        str_reset(&param_component->tag);
        str_reset(&param_component->param_type);
        str_reset(&param_component->param_name);
    }

    return RET_OK;
}

static function_component_t* aidl_parser_create_function_component() {
    function_component_t* ret = (function_component_t*)malloc(sizeof(function_component_t));
    return_value_if_fail(ret != NULL, NULL);

    str_init(&ret->return_type, 20);
    str_init(&ret->function_name, 20);
    ret->param_list = slist_create((tk_destroy_t)param_component_destroy_t, NULL);
    return ret;
}

static ret_t aidl_parser_destroy_function_component(function_component_t* function_info) {
    return_value_if_fail(function_info != NULL, RET_OK);

    slist_destroy(function_info->param_list);
    str_reset(&function_info->return_type);
    str_reset(&function_info->function_name);
    
    free(function_info);

    return RET_OK;
}

static param_component_t* aidl_parser_parse_param(const char* param_declaration) {
    if (param_declaration == NULL || strlen(param_declaration) == 0) {
        return NULL;
    }

    param_component_t* param_component = (param_component_t*)malloc(sizeof(param_component_t));
    return_value_if_fail(param_component != NULL, NULL);
    str_init(&param_component->tag, 20);
    str_init(&param_component->param_type, 20);
    str_init(&param_component->param_name, 20);

    aidl_parser_t aidl_parser;
    aidl_parser_t* aidl_parser_ptr = aidl_parser_init(&aidl_parser, param_declaration, strlen(param_declaration), " ");

    const char* token = aidl_parser_next(aidl_parser_ptr);
    if (token != NULL && strlen(token)) {
        bool_t has_tag = FALSE;
        if (strcmp(token, "in") == 0 || strcmp(token, "out") == 0 || strcmp(token, "inout") == 0) {
            has_tag = TRUE;
        }

        if (has_tag) {
            str_set_with_len(&param_component->tag, token, strlen(token));
            const char* param_type = aidl_parser_next(aidl_parser_ptr);
            if (param_type != NULL && strlen(param_type) > 0) {
                str_set_with_len(&param_component->param_type, param_type, strlen(param_type));
            }
        } else {
            str_set_with_len(&param_component->tag, "in", strlen("in"));
            str_set_with_len(&param_component->param_type, token, strlen(token));
        }
    }

    const char* param_name = aidl_parser_next(aidl_parser_ptr);
    if (param_name != NULL && strlen(param_name) > 0) {
        str_set_with_len(&param_component->param_name, param_name, strlen(param_name));
    }

    aidl_parser_deinit(aidl_parser_ptr);
    return param_component;
}

static function_component_t* aidl_parser_parse_funtion(const char* funtion_declaration) {
    return_value_if_fail(funtion_declaration != NULL || strlen(funtion_declaration), NULL);

    function_component_t* function_compent = aidl_parser_create_function_component();
    return_value_if_fail(function_compent != NULL, NULL);

    aidl_parser_t aidl_parser;
    aidl_parser_t* aidl_parser_ptr = aidl_parser_init(&aidl_parser, funtion_declaration, strlen(funtion_declaration), " (,)");

    const char* return_type = aidl_parser_next(aidl_parser_ptr);
    if (return_type != NULL && strlen(return_type) > 0) {
        str_set_with_len(&function_compent->return_type, return_type, strlen(return_type));
    }

    const char* function_name = aidl_parser_next(aidl_parser_ptr);
    if (function_name != NULL && strlen(function_name) > 0) {
        str_set_with_len(&function_compent->function_name, function_name, strlen(function_name));
    }
    
    while (aidl_parser_has_more(aidl_parser_ptr)) {
        const char* param_declaration = aidl_parser_next_until(aidl_parser_ptr, ",)");
        if (param_declaration != NULL && strlen(param_declaration) > 0) {
            param_component_t* param_component = aidl_parser_parse_param(param_declaration);
            slist_append(function_compent->param_list, param_component);
        }
    }

    aidl_parser_deinit(aidl_parser_ptr);
    return function_compent;
}

static function_component_t* aidl_parser_next_function(aidl_parser_t* aidl_parser) {
    while (aidl_parser_has_more(aidl_parser)) {
        const char* funtion_declaration = aidl_parser_next_until(aidl_parser, ";");
        if (funtion_declaration != NULL && strlen(funtion_declaration) > 0) {
            return aidl_parser_parse_funtion(funtion_declaration);
        }
    }

    return NULL;
}

static void aidl_parser_convert_package(aidl_parser_t* aidl_parser) {
    return_if_fail(aidl_parser != NULL);
    
    const char* package_name = aidl_parser_first_package(aidl_parser);
    if (package_name != NULL) {
        aidl_parser->builder->aidl_parser_on_package(aidl_parser->builder->ctx, package_name);
    }
    
    const char* next_package_name = aidl_parser_next_package(aidl_parser);
    while (next_package_name != NULL)
    {
        aidl_parser->builder->aidl_parser_on_package(aidl_parser->builder->ctx, package_name);
        next_package_name = aidl_parser_next_package(aidl_parser);
    }

    return;
}

static void aidl_parser_convert_import(aidl_parser_t* aidl_parser) {
    return_if_fail(aidl_parser != NULL);
    
    const char* class_name = aidl_parser_first_import(aidl_parser);
    if (class_name != NULL) {
        aidl_parser->builder->aidl_parser_on_import(aidl_parser->builder->ctx, class_name);
    }
    
    const char* next_class_name = aidl_parser_next_import(aidl_parser);
    while (next_class_name != NULL)
    {
        aidl_parser->builder->aidl_parser_on_import(aidl_parser->builder->ctx, next_class_name);
        next_class_name = aidl_parser_next_import(aidl_parser);
    }

    return;
}

static void aidl_parser_convert_function(aidl_parser_t* aidl_parser) {
    return_if_fail(aidl_parser != NULL);
    
    const char* interface_name = aidl_parser_interface_name(aidl_parser);
    

    if (interface_name != NULL) {
        str_t str_interface_name;
        str_init(&str_interface_name, 20);
        str_set_with_len(&str_interface_name, interface_name, strlen(interface_name));

        function_component_t* next_function_component = aidl_parser_next_function(aidl_parser);
        while (next_function_component != NULL)
        {
            aidl_parser->builder->aidl_parser_on_function(aidl_parser->builder->ctx, 
            str_interface_name.str, next_function_component);

            aidl_parser_destroy_function_component(next_function_component);
            next_function_component = NULL;

            next_function_component = aidl_parser_next_function(aidl_parser);
        }

        str_reset(&str_interface_name);
    }

    return;
}

static ret_t aidl_parser_deinit(aidl_parser_t* aidl_parser) {
    return_value_if_fail(aidl_parser != NULL, RET_BAD_PARAMS);
    str_reset(&(aidl_parser->token));
    memset(aidl_parser, 0, sizeof(aidl_parser_t));

    return RET_OK;
}

ret_t aidl_parser_convert(const char* str, uint32_t size, builder_t* builder) {   
    return_value_if_fail(str != NULL && builder != NULL, RET_BAD_PARAMS);

    aidl_parser_t aidl_parser;
    aidl_parser_t* p = aidl_parser_init(&aidl_parser, str, size, " ;\n{}()");
    aidl_parser_set_builder(p, builder);
    aidl_parser_convert_package(p);
    aidl_parser_convert_import(p);
    aidl_parser_convert_function(p);
    aidl_parser_deinit(p);

    return RET_OK;
}